﻿using Xejen.Communication.Configuration;
using Xejen.Communication.Endpoints;
using Xejen.Communication.Ftp.Abstract;
using Xejen.Communication.Ftp.Configuration;
using Xejen.Logger;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Xejen.Communication.Ftp.Communicator
{
    /// <summary>
    /// 基于Ftp协议实现的通讯器
    /// </summary>
    /// <typeparam name="TFtpInvoker"><inheritdoc cref="IFtpInvoker" path="/summary"/></typeparam>
    /// <creator>marc</creator>
    public class FtpCommunicator<TFtpInvoker> : Communicator<FtpConfig, HeartbeatConfig, FtpAddress>, ICommunicator<FtpConfig, HeartbeatConfig>, IFtpCommunicator
        where TFtpInvoker : IFtpInvoker
    {
        /// <inheritdoc cref="IFtpInvokerCreator{TFtpInvoker}"/>
        protected IFtpInvokerCreator<TFtpInvoker> FtpInvokerCreator { get; }

        /// <inheritdoc/>
        protected override JsonSerializerOptions JsonSerializerOptions => Config.JsonSerializerOptions;

        /// <inheritdoc cref="FtpCommunicator{TFtpInvoker}"/>
        /// <param name="config"><inheritdoc cref="Communicator{TConfig, HeartbeatConfig, TAddress}.Config" path="/summary"/></param>
        /// <param name="ftpInvokerCreator"><inheritdoc cref="FtpInvokerCreator" path="/summary"/></param>
        /// <param name="loggerManager"><inheritdoc cref="ILoggerManager" path="/summary"/></param>
        public FtpCommunicator(
            FtpConfig config,
            IFtpInvokerCreator<TFtpInvoker> ftpInvokerCreator,
            ILoggerManager loggerManager) : base(config, loggerManager)
        {
            Check.NotNull(config, nameof(config));
            Check.NotNull(ftpInvokerCreator, nameof(ftpInvokerCreator));
            Check.NotNull(loggerManager, nameof(loggerManager));

            Config = config;
            FtpInvokerCreator = ftpInvokerCreator;
            Logger = loggerManager.CreateLogger<FtpCommunicator<TFtpInvoker>>();
        }

        /// <inheritdoc/>
        public virtual async Task<FtpResponse> UploadFileAsync(FtpAddress address, string filePath, IProgress<IProgressData> progress = null, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).UploadFileAsync(address, filePath, progress, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, GetTimeout(timeout), filePath, cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse> AppendFileAsync(FtpAddress address, string filePath, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).AppendFileAsync(address, filePath, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, GetTimeout(timeout), filePath, cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse> DownloadFileAsync(FtpAddress address, string localFilePath, IProgress<IProgressData> progress = null, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).DownloadFileAsync(address, localFilePath, progress, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, GetTimeout(timeout), localFilePath, cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse> MakeDirectoryAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).MakeDirectoryAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse> RenameFileAsync<TInput>(FtpAddress address, TInput input, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).RenameFileAsync(address, input, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, GetTimeout(timeout), input, cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse> DeleteFileAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).DeleteFileAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse<DateTime?>> GetDateTimestampAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).GetDateTimestampAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse<long?>> GetFileSizeAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).GetFileSizeAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse<string[]>> ListDirectoryAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).ListDirectoryAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse<string[]>> ListDirectoryDetailsAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).ListDirectoryDetailsAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse<string>> PrintWorkingDirectoryAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).PrintWorkingDirectoryAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse> RemoveDirectoryAsync(FtpAddress address, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;

            var task = FtpInvokerCreator.Create(config).RemoveDirectoryAsync(address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);
        }
        /// <inheritdoc/>
        public virtual async Task<FtpResponse<string>> UploadFileWithUniqueNameAsync(FtpAddress address, string filePath, FtpConfig ftpConfig = null, int timeout = -1, CancellationToken cancellationToken = default)
        {
            Check.NotNull(address, nameof(address));

            var config = ftpConfig ?? Config;
            var task = FtpInvokerCreator.Create(config).UploadFileWithUniqueNameAsync(address, filePath, timeout: GetTimeout(timeout), cancellationToken: cancellationToken);

            return await LogInvokeAsync(task, address, GetTimeout(timeout), filePath, cancellationToken);
        }
    }
}
